#include					"IA.hpp"
#include					"Goban.hpp"
#include					<iostream>

IA::IA(Goban &goban, std::string const &stonePath, Arbitre &arbitre, short int depth)
	:	_goban(goban), _arbitre(arbitre), _depth(depth), _squareMap(goban.getSquareMap())
{
	this->_stonePath = stonePath;
	this->_deadStone = 0;
	this->AIPlayer = false;
	srand(75);
	playAround.insert(std::pair<int, int>(0, 1));
	playAround.insert(std::pair<int, int>(1, -1));
	playAround.insert(std::pair<int, int>(2, GOBAN_BOX_PER_LINE));
	playAround.insert(std::pair<int, int>(3, -GOBAN_BOX_PER_LINE));
	playAround.insert(std::pair<int, int>(4, GOBAN_BOX_PER_LINE + 1));
	playAround.insert(std::pair<int, int>(5, -GOBAN_BOX_PER_LINE - 1));
	playAround.insert(std::pair<int, int>(6, GOBAN_BOX_PER_LINE + 1));
	playAround.insert(std::pair<int, int>(7, -GOBAN_BOX_PER_LINE - 1));
}

IA::~IA()
{
}

void					IA::putStone(int key)
{
	int					ret;
	int					pos;

	pos = minMaxRoot();
	if (pos < 0)
		pos = this->doNotPlayLikeAMartens(key);
	ret = _arbitre.check(pos, _goban.getSquareInfo(pos), _deadStone, (((_stonePath) == (BLACK_STONE_PATH)) ? false : true));
	_squareMap[pos]->putStone(_stonePath);
}

void					IA::removeStone(int key)
{

}

int					IA::minMaxRoot()
{
	Goban					*tmp_map;
	int						ret;
	int						score = 0;
	int						tmp_pos = 0;
	int						final_pos = 0;
	int						i = 0;

	while ((tmp_map = this->simulate(&this->_goban, tmp_pos)) != NULL)
	{
		ret = this->minMaxTree(tmp_map, _depth, this->getScore(tmp_pos, tmp_map->getMapInfo()));
		if (i == 0 || (i > 0 && ret > score))
		{
			score = ret;
			final_pos = tmp_pos;
		}
		delete tmp_map;
		tmp_pos++;
		i++;
	}
	if (i == 0)
		return (-1);
	return (final_pos);
}

int					IA::minMaxTree(Goban *map, short int depth, int local_score)
{
	Goban					*tmp_map;
	int						ret;
	int						score = 0;
	int						pos = 0;

	if (depth <= 0)
		return (0);
	this->switchPlayer();
	while ((tmp_map = this->simulate(map, pos)) != NULL)
	{
		ret = this->minMaxTree(tmp_map, depth - 1, this->getScore(pos, map->getMapInfo()));
		if (AIPlayer == false)
			score = std::max(score, score + ret);
		else if (AIPlayer == true)
			score = std::min(score, score + ret);
		delete tmp_map;
		pos++;
	}
	return (score + local_score);
}


Goban						*IA::simulate(Goban *prev_map, int &pos)
{
	Goban					*new_map;
	Box						*map = prev_map->getMapInfo();
	std::map<int, Square*>	new_SquareMap;
	std::map<int, Square*>	prev_SquareMap = prev_map->getSquareMap();

	while (pos <= GOBAN_LAST_BOX_NUM)
	{
		if (!(prev_SquareMap[pos]->displayable()) &&
			(((map[pos].states & WHITE_LINE) == WHITE_LINE) ||  
			((map[pos].states & BLACK_LINE) == BLACK_LINE) || 
			((map[pos].states & WHITE_TAKE) == WHITE_TAKE) || 
			((map[pos].states & BLACK_TAKE) == BLACK_TAKE)))
		{
			new_map = new Goban(*prev_map);
			new_SquareMap = new_map->getSquareMap();
			if (AIPlayer == false)
				new_SquareMap[pos]->putStone(BLACK_STONE_PATH);
			else
				new_SquareMap[pos]->putStone(WHITE_STONE_PATH);
			return (new_map);
		}
		pos++;
	}
	return (NULL);
}

int							IA::getScore(int &pos, Box *map)
{
	int						score = 0;

	if ((map[pos].states & WHITE_LINE) == WHITE_LINE)
	{
		if (AIPlayer == true)
			score += 40;
		else
			score += 20;
	}
	if ((map[pos].states & WHITE_TAKE) == WHITE_TAKE)
	{
		if (AIPlayer == true)
			score += 40;
		else
			score += 20;
	}
	if ((map[pos].states & BLACK_LINE) == BLACK_LINE)
	{
		if (AIPlayer == true)
			score += 20;
		else
			score += 40;
	}
	if ((map[pos].states & BLACK_TAKE) == BLACK_TAKE)
	{
		if (AIPlayer == true)
			score += 20;
		else
			score += 40;
	}
	return (score);
}

void						IA::switchPlayer()
{
	if (AIPlayer == false)
		AIPlayer = true;
	else
		AIPlayer = false;
}

int							IA::doNotPlayLikeAMartens(int pos)
{
	int						ret = 0;
	int						random = rand() % 8;

	for (int i = 0; i < GOBAN_LAST_BOX_NUM; i++)
	{
		ret = pos + (this->playAround[random] * i);
		if (!KEY_VALIDITY(ret))
		{
			random = rand() % 8;
			i--;
			continue;
		}
		if (!this->_squareMap[ret]->displayable())
			return (ret);
	}
}

int						IA::getDeadStone() const
{
	return (this->_deadStone);
}